﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using System.IO;
using DocumentFormat.OpenXml.Wordprocessing;
using NotesFor.HtmlToOpenXml;


namespace TIT.OpenXml.Document
{
    public static class OpenXmlDocument
    {
        public static OpenXmlDocumentStream Open(Stream document) 
        {
            return new OpenXmlDocumentStream(document);
        }
    }
    public class OpenXmlDocumentStream : IDisposable
    {
        private WordprocessingDocument _document { get; set; }
        private List<IElement> _elements { get; set; }
        private MemoryStream _memoryStream { get; set; }
        private Queue<IElement> _transformHistory { get; set; }
        
        public OpenXmlDocumentStream(Stream data)
        {
            _memoryStream = data as MemoryStream;
            _document = WordprocessingDocument.Open(_memoryStream, true);
            _elements = new List<IElement>();
        }
        public OpenXmlDocumentStream AddPlaceHolder(string placeHolderId, string value) 
        {
            var placeHolder = new PlaceHolderElement();
            placeHolder.Id = placeHolderId;
            placeHolder.Value = value;
            _elements.Add(placeHolder);

            return this;
        }
        public OpenXmlDocumentStream AddPlaceHolder(string placeHolderId, string value,bool removeRow)
        {
            var placeHolder = new PlaceHolderElement();
            placeHolder.Id = placeHolderId;
            placeHolder.Value = value;
            placeHolder.NoTableRow = removeRow;
            _elements.Add(placeHolder);

            return this;
        }
        public OpenXmlDocumentStream AddHtmlParagraph(string placeHolderId, string htmlSource) 
        {
            var placeHolder = new HTMLParagraphElement();
            placeHolder.Id = placeHolderId;
            placeHolder.Value = htmlSource;
            _elements.Add(placeHolder);
            
            return this;
        }
        public void Finish() 
        {
            foreach (var element in _elements) 
            {
                element.Document = _document;
                element.Transform();
            }
        }
        public void Save(string filename) 
        {
            _document.MainDocumentPart.Document.Save();
            
            var file = File.Open(filename, FileMode.OpenOrCreate);
            file.Position = 0;
            _memoryStream.WriteTo(file);
            file.Flush();
            file.Close();
        }
        public void Save() 
        {
            _document.MainDocumentPart.Document.Save();
        }
        public void Dispose()
        {
            
        }
    }
    interface IElement
    {
        WordprocessingDocument Document { get; set; }
        void Transform();
    }
    class HTMLParagraphElement : IElement 
    {
        public string Id { get; set; }
        public string Value { get; set; }
        public WordprocessingDocument Document { get; set; }
        
        private string FormattedId
        {
            get
            {
                return "{@{0}}".Replace("{0}", Id);
            }
        }

        public void Transform()
        {

            var tag = Document.MainDocumentPart.Document
                                    .Descendants<Tag>()
                                        .Where(r => r.Val == FormattedId).ToList();
            if (tag.Count == 0)
            {
                throw new ArgumentException("Paramêtro inexistente - " + FormattedId);
            }

            foreach (var t in tag)
            {
                var stdBlock = t.Ancestors<SdtBlock>().FirstOrDefault();
                
                //conversão html to openxml
                var htmlConverter = new HtmlConverter(Document.MainDocumentPart);
                var paragraphs = htmlConverter.Parse(Value.Replace("div","p"));
                var pP = stdBlock.Descendants<Paragraph>().FirstOrDefault();
                foreach (Paragraph paragraph in paragraphs)
                {
                    if (pP != null) 
                    {
                        if (pP.ParagraphProperties != null)
                        {
                            paragraph.ParagraphProperties = (ParagraphProperties)pP.ParagraphProperties.Clone();
                        }
                    }
                    stdBlock.Parent.InsertAfter<Paragraph>(paragraph, stdBlock);
                }
                stdBlock.Parent.RemoveChild<SdtBlock>(stdBlock);
            }
        }

    }
    class PlaceHolderElement : IElement
    {
        public string Id { get; set; }
        public string Value { get; set; }
        public WordprocessingDocument Document { get; set; }
        public bool NoTableRow { get; set; }
        private string FormattedId 
        {
            get 
            {
                return "{@{0}}".Replace("{0}", Id);
            }
        }
        public void Transform() 
        {
            var tag = Document.MainDocumentPart.Document
                                    .Descendants<Tag>()
                                        .Where(r => r.Val == FormattedId).ToList();
            if (tag.Count == 0)
            {
                throw new ArgumentException("Paramêtro inexistente - " + FormattedId);
            }
           
            foreach (var t in tag)
            {
                if (String.IsNullOrEmpty(Value))
                {
                    if (NoTableRow)
                    {

                        var row = t.Ancestors<TableRow>().FirstOrDefault();
                        if (row != null)
                        {
                            row.Parent.RemoveChild<TableRow>(row);
                        }
                    }
                }
                else
                {
                    var stdBlock = t.Ancestors<SdtBlock>().FirstOrDefault();

                    if (stdBlock != null)
                    {
                        var text = new Text();
                        text.Text = Value;
                        var run = new Run();
                        run.RunProperties = new RunProperties();
                        var rP = stdBlock.Descendants<Run>().FirstOrDefault();
                        if (rP != null)
                        {
                            if (rP.RunProperties != null)
                            {
                                run.RunProperties = (RunProperties)rP.RunProperties.Clone();
                            }
                        }
                        run.AppendChild<Text>(text);
                        var paragraph = new Paragraph();
                        paragraph.ParagraphProperties = new ParagraphProperties();
                        var pP = stdBlock.Descendants<Paragraph>().FirstOrDefault();
                        if (pP != null)
                        {
                            if (pP.ParagraphProperties != null)
                            {
                                paragraph.ParagraphProperties = (ParagraphProperties)pP.ParagraphProperties.Clone();
                            }
                        }
                        paragraph.AppendChild<Run>(run);

                        stdBlock.Parent.ReplaceChild<SdtBlock>(paragraph, stdBlock);
                    }
                    var stdRun = t.Ancestors<SdtRun>().FirstOrDefault();
                    if (stdRun != null)
                    {
                        var text = new Text();
                        text.Text = Value;
                        var run = new Run();
                        run.RunProperties = new RunProperties();
                        var rP = stdRun.Descendants<Run>().FirstOrDefault();
                        if (rP != null)
                        {
                            if (rP.RunProperties != null)
                            {
                                run.RunProperties = (RunProperties)rP.RunProperties.Clone();
                            }
                        }
                        run.AppendChild<Text>(text);
                        stdRun.Parent.ReplaceChild<SdtRun>(run, stdRun);

                    }
                }
                
            }

        }
    }
}
